package cn.tedu.reflection;

import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

/**本类用于反射技术的测试类*/
public class TestReflect {
    /**单元测试方法:是Java中最小的测试单位，使用灵活，推荐使用
     * 语法要求:public + void + 没有参数 + @Test
     * 注意:使用时需要导包:Add JUnit4 to classpath
     * 导包以后的效果:import org.junit.Test;
     * 执行方式:选中方法名前的绿色小三角，成功运行后会有绿色小对钩*/
    /**测试1:通过单元测试方法，获取目标类Student对应的字节码对象*/
    @Test
    public void getClazz() throws ClassNotFoundException {
        Class.forName("cn.tedu.reflection.Student");
        //获取字节码对象的3种方式
        Class<?> clazz1 = Class.forName("cn.tedu.reflection.Student");
        Class<?> clazz2 = Student.class;
        Class<?> clazz3 = new Student().getClass();

        //直接打印字节码对象，注意最前面有个class,这是一个对象
        System.out.println(clazz1);
        //通过字节码对象获取的目标类Student的全路径名
        System.out.println(clazz1.getName());
        System.out.println(clazz1.getSimpleName());//通过字节码文件获取类名
        //通过字节码对象获取目标类的包对象
        System.out.println(clazz3.getPackage());
        //先通过字节码对象获取目标类的包对象，再通过包对象获取包名
        System.out.println(clazz3.getPackage().getName());

    }
    /**测试2:通过反射技术，获取目标类中的成员变量*/
    @Test
    public void getFie() throws ClassNotFoundException {
        //1.获取字节码对象
        Class<?> clazz = Class.forName("cn.tedu.reflection.Student");
        //2.通过字节码对象获取目标类中成员变量
        Field[] fs = clazz.getFields();
        /**注意:目前我们只能获取到用public修饰的成员变量
         * 比如默认或者private修饰符修饰的成员变量是获取不到的*/
        //3.遍历数组，获取每个字段对象，并进行进一步操作
        for (Field f:fs) {
            //通过本轮循环到的字段对象，进一步获取字段的名字与字段的类型
            System.out.println(f.getName());
            System.out.println(f.getType());
        }
    }
    /**测试3:通过单元测试方法，练习引用类型数组的定义与遍历*/
    @Test
    public void getStu(){
        //创建Student类对应的3个对象
        Student s1 = new Student("张三", 3);
        Student s2 = new Student("李四", 4);
        Student s3 = new Student("王五", 5);
        //定义一个数组，将刚刚创建好的3个对象存入
        Student[] stu = {s1,s2,s3};
        //打印查看数组中所有对象
        System.out.println(Arrays.toString(stu));
        //遍历数组，拿到对象
        for (Student ss:stu) {
            System.out.println(stu);//直接打印本轮循环到的学生对象
            ss.play();//通过本轮循环到的学生对象调用学生的play()
            System.out.println(ss.age);//通过本轮循环到的学生对象查看当前对象的属性值
        }
    }
    /**测试4:通过反射技术，获取目标类中的成员方法*/
    @Test
    public void getFuction(){
        //获取字节码对象
        Class<?> clazz = Student.class;
        //2.通过字节码对象获取目标类中的成员方法
        Method[] ms = clazz.getMethods();
        //3.通过高效for循环遍历数组，拿到每一个方法对象，做进一步操作
        for (Method m:ms) {
            System.out.println(m);//查看循环获取到的每个方法对象
            System.out.println(m.getName());//通过方法对象，获取当前遍历到的方法名
            Class<?>[] pt = m.getParameterTypes();//通过方法对象获取当前方法的参数类型
            System.out.println(Arrays.toString(pt));//直接打印查看参数类型
        }
    }
    /**测试5.通过反射技术，获取目标类中的构造方法*/
    @Test
    public void getCons(){
        //获取字节码对象
        Class<?> clazz = new Student().getClass();
        //通过字节码对象，获取获取目标类中的构造方法们
        Constructor<?>[] cs = clazz.getConstructors();
        //遍历构造函数数组，获取每一个构造函数对象，做进一步操作
        for (Constructor c:cs) {
            System.out.println(c);//直接打印构造函数对象
            System.out.println(c.getName());//通过遍历到的构造函数对象，获取构造函数名
            Class[] pt = c.getParameterTypes();//通过遍历到的构造函数对象，获取构造函数参数类型
            System.out.println(Arrays.toString(pt));//打印查看当前构造函数的参数类型
        }
    }
    /**测试6.通过反射技术，创建目标类Student的对象*/
    @Test
    public void getObject() throws Exception{
        Class<?> clzz= Student.class;
        //通过字节码对象，创建目标类的对象--默认使用无参构造
        /**反射创建对象方案1:通过字节码对象触发目标类中的无参构造来创建对象*/
        Object o = clzz.newInstance();
        System.out.println(o);

        /**反射创建对象方案2:通过触发目标类中其他的含参构造来创建对象
         * 思路:
         * 1.先获取指定的构造函数对象，注意传入对应的构造函数参数，传入的是字节码对象
         * 2.通过刚刚获取到的构造函数对象来创建目标类Student的对象*/
        //获取目标类中指定的全参构造
        Constructor<?> c = clzz.getConstructor(String.class, int.class);
        //通过刚刚获取到的构造函数对象来创建目标类对象
        Object o2 = c.newInstance("赵六", 6);
        System.out.println(o2);

        Constructor<?> cc = clzz.getConstructor(String.class);
        Object o3 = cc.newInstance("王五");
        System.out.println(o3);
    }
}
